//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Validation Application Block
//===============================================================================

using System;
using System.Collections.Generic;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using EntLibContrib.Validation.Configuration;

namespace EntLibContrib.Validation.Validators
{
	/// <summary>
	/// Composite RuleSet Validator
	/// </summary>
	[ConfigurationElementType(typeof(CompositeRuleSetValidatorData))]
	public class CompositeRuleSetValidator : Validator
	{
		#region Fields
		private Type targetType;
		private ValidationSpecificationSource source;
		private string[] targetRuleSets;
		private IConfigurationSource configurationSource;
		private string configurationSourceName;
		#endregion

		#region Construction
		/// <summary>
		/// Initializes a new instance of the <see cref="CompositeRuleSetValidator"/> class.
		/// </summary>
		/// <param name="targetType">Type of the target.</param>
		/// <param name="targetRuleSets">The target ruleSets.</param>
		public CompositeRuleSetValidator(Type targetType, string[] targetRuleSets)
			: this(targetType, targetRuleSets, ValidationSpecificationSource.Both)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="CompositeRuleSetValidator"/> class.
		/// </summary>
		/// <param name="targetType">Type of the target.</param>
		/// <param name="targetRuleSets">The target ruleSets.</param>
		/// <param name="source">The source.</param>
		/// <param name="configurationSource">The configuration source.</param>
		public CompositeRuleSetValidator(Type targetType, string[] targetRuleSets, ValidationSpecificationSource source, IConfigurationSource configurationSource)
			: this(targetType, targetRuleSets, source)
		{
			this.configurationSource = configurationSource;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="CompositeRuleSetValidator"/> class.
		/// </summary>
		/// <param name="targetType">Type of the target.</param>
		/// <param name="targetRuleSets">The target ruleSets.</param>
		/// <param name="source">The source.</param>
		/// <param name="configurationSourceName">Name of the configuration source.</param>
		public CompositeRuleSetValidator(Type targetType, string[] targetRuleSets, ValidationSpecificationSource source, string configurationSourceName)
			: this(targetType, targetRuleSets, source)
		{
			this.configurationSourceName = configurationSourceName;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="CompositeRuleSetValidator"/> class.
		/// </summary>
		/// <param name="targetType">Type of the target.</param>
		/// <param name="targetRuleSets">The target ruleSets.</param>
		/// <param name="source">The source.</param>
		public CompositeRuleSetValidator(Type targetType, string[] targetRuleSets, ValidationSpecificationSource source)
			: base(null, null)
		{
			ArgumentValidation.Validate("targetType", targetType, DefaultValidators.NotNullValidator);
			ArgumentValidation.Validate("targetRuleSets", targetRuleSets, DefaultValidators.NotNullValidator);
			ArgumentValidation.Validate("source", source, DefaultValidators.EnumDefinedValidator);

			this.targetType = targetType;
			this.targetRuleSets = targetRuleSets;
			this.source = source;
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Gets the target ruleSets.
		/// </summary>
		/// <param name="targetRuleSets">The target ruleSets.</param>
		/// <returns></returns>
		public static string[] GetTargetRuleSets(string targetRuleSets)
		{
			if (targetRuleSets == null)
			{
				return new string[0];
			}
			else
			{
				List<string> items = new List<string>();

				foreach (string targetRuleSet in targetRuleSets.Split(','))
				{
					items.Add(targetRuleSet.Trim());
				}

				return items.ToArray();
			}
		}
		#endregion

		#region Protected Methods
		/// <summary>
		/// Gets the message template to use when logging results no message is supplied.
		/// </summary>
		/// <value></value>
		protected override string DefaultMessageTemplate
		{
			get { return null; }
		}

		/// <summary>
		/// Implements the validation logic for the receiver.
		/// </summary>
		/// <param name="objectToValidate">The object to validate.</param>
		/// <param name="currentTarget">The object on the behalf of which the validation is performed.</param>
		/// <param name="key">The key that identifies the source of <paramref name="objectToValidate"/>.</param>
		/// <param name="validationResults">The validation results to which the outcome of the validation should be stored.</param>
		/// <remarks>
		/// Subclasses must provide a concrete implementation the validation logic.
		/// </remarks>
		public override void DoValidate(object objectToValidate, object currentTarget, string key, ValidationResults validationResults)
		{
			foreach (string targetRuleSet in targetRuleSets)
			{
				Validator validator = null;

				switch (source)
				{
					case ValidationSpecificationSource.Attributes:
						validator = ValidationFactory.CreateValidatorFromAttributes(targetType, targetRuleSet);
						break;

					case ValidationSpecificationSource.Configuration:
						validator = ValidationFactory.CreateValidatorFromConfiguration(targetType, targetRuleSet, SafeConfigurationSource);
						break;

					default:
						validator = ValidationFactory.CreateValidator(targetType, targetRuleSet, SafeConfigurationSource);
						break;
				}

				validator.Validate(objectToValidate, validationResults);
			}
		}

		/// <summary>
		/// Gets the safe configuration source.
		/// </summary>
		/// <value>The safe configuration source.</value>
		protected virtual IConfigurationSource SafeConfigurationSource
		{
			get
			{
				if (configurationSource == null)
				{
					if (string.IsNullOrEmpty(configurationSourceName))
					{
						configurationSource = ConfigurationSourceFactory.Create();
					}
					else
					{
						configurationSource = ConfigurationSourceFactory.Create(configurationSourceName);
					}
				}

				return configurationSource;
			}
		}
		#endregion
	}
}
